// AudioPlug library definitions
// 2010-05-12 <info@theater4all.nl> http://opensource.org/licenses/mit-license.php

#include "AudioPlug.h"

void AudioPlug::init() 
{
  // set output ports
  pinMode(CS_PIN,OUTPUT);     // CS 
  digitalWrite(CS_PIN, 1);

  pinMode(MUTE_PIN,OUTPUT);   // MUTE 
  digitalWrite(MUTE_PIN, 1);  // set MUTEOFF [negative active]
  Mute = false;

  pinMode(DATA_PIN,OUTPUT);   // Data
  digitalWrite(DATA_PIN, LOW);

  pinMode(CLK_PIN,OUTPUT);    // Clk
  digitalWrite(CLK_PIN, LOW);

  // init globals
  nrBoards = 0;
  nrAudioChannels = 0;
  masterVolume = 255;
  for (uint8_t i=0; i<MAXBOARDS; i++) {
	boardType[i] = 0;
  }  
} 

void AudioPlug::addBoard(uint8_t newBoardType)
{
    if (nrBoards >= MAXBOARDS) return;
	if (newBoardType == TWOCHANNELS)  nrAudioChannels += 2;
	if (newBoardType == OLDFOURCHANNELS)  nrAudioChannels += 4;
	if (newBoardType == FOURCHANNELS)  nrAudioChannels += 4;
	
	boardType[nrBoards++] = newBoardType;
    setChannelmap();
}


void AudioPlug::setChannelmap()
{
    uint8_t index = 0;
	uint8_t base = nrAudioChannels;
	
	// build channels map
	// NOTE: last channel is shifted out first !
    for (int b=nrBoards-1; b>=0; b--) {
		if (boardType[b] == TWOCHANNELS) {
			base -= 2;
			hardwareChannelPos[index++] = base + 0;
			hardwareChannelPos[index++] = base + 1;
		}
		else if (boardType[b] == FOURCHANNELS) {
			// 4,1,3,2 op het nieuwe board
			base -= 4;
			hardwareChannelPos[index++] = base + 1;
			hardwareChannelPos[index++] = base + 2;
			hardwareChannelPos[index++] = base + 0;
			hardwareChannelPos[index++] = base + 3;
		}
		else if (boardType[b] == OLDFOURCHANNELS) {
			base -= 4;
			hardwareChannelPos[index++] = base + 3;
			hardwareChannelPos[index++] = base + 2;
			hardwareChannelPos[index++] = base + 1;
			hardwareChannelPos[index++] = base + 0;
		}		
    }
}


void AudioPlug::showChannelMap()
{
  for (uint8_t i=0; i<nrAudioChannels; i++) {
	Serial.print(i,DEC);
	Serial.print(" = ");
    Serial.println(hardwareChannelPos[i], DEC);
  }
}



// shift out 1 byte data MSB first
void AudioPlug::sendByte(uint8_t val) 
{
   for (uint8_t i=0; i<8; i++)  {	
    digitalWrite(DATA_PIN, !!(val & (1 << (7 - i))));	// data out
    digitalWrite(CLK_PIN, HIGH);                        // clock it
    digitalWrite(CLK_PIN, LOW);		                    // reset clk 
  }
}

// send all volume data
void AudioPlug::sendVolumeData(void) 
{
  unsigned int volVal;
	
  // send
  digitalWrite(CS_PIN, 0);
  for (uint8_t i=0; i<nrAudioChannels; i++) {
    volVal = VolumeBuffer[hardwareChannelPos[i]];
	volVal *= masterVolume;
	volVal /= 255;
    sendByte((byte)volVal);
  }
  digitalWrite(CS_PIN, 1); 
  digitalWrite(DATA_PIN, LOW);	// data out reset
}

void AudioPlug::setVolume(uint8_t channel, uint8_t value)
{
  if (channel >= MAXCHANNELS) return;
  VolumeBuffer[channel] = value;
}

uint8_t AudioPlug::getVolume(uint8_t Channel)
{
  return (VolumeBuffer[Channel]);
}

void AudioPlug::setMaster(uint8_t vol)
{
	masterVolume = vol;
	sendVolumeData();
}

void AudioPlug::setMute(bool value)
{
  // Mute is negative active so invert
  digitalWrite(MUTE_PIN, !value);
  Mute = value;
}

bool AudioPlug::getMute(void)
{
	return Mute;
}

// convert signed float in the range of [ -95 to + 35 ] to a channel Byte Value
uint8_t AudioPlug::dB2Byte (int dBValue)
{
	if (dBValue >= 31)
		return 255;
	if (dBValue <= -95)
		return 0;
		
	return (uint8_t)(255 + (dBValue - 31)*2);
}


// convert a Byte Value to the corresponding dB value
int AudioPlug::byte2dB(uint8_t Value)
{
	// calc val to dB
	return (31 - ((255 - Value) >> 1)); 
}
